﻿#include "pch.h"
#include "Game.h"

BaseGameObject* Game::sBaseGameObjects[Const::GAME_BOARD_GRID_COUNT_V][Const::GAME_BOARD_GRID_COUNT_H];

/**
 * 构造方法。
 * @param callback  回调
 * @param drawer    绘图者
 * @param gameBoard 游戏区域
 */
Game::Game(CWnd* callback, CDC* drawer, CWnd* gameBoard) :
    mBoxCount(0),
    mCallback(callback),
    mDrawer(drawer),
    mGameBoard(gameBoard),
    mLevel(1),
    mMan(NULL),
    mMoveCount(0),
    mStatus(END) {
    // 进入第1关
    this->EnterLevel();
}

/**
 * 析构方法。
 */
Game::~Game() {
    this->Clear();
}

/**
 * 清除所有对象。
 */
void Game::Clear() {
    // 删除所有游戏物体
    for (int i = 0; i < Const::GAME_BOARD_GRID_COUNT_V; i++) {
        for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
            if (sBaseGameObjects[i][j] != NULL) {
                delete sBaseGameObjects[i][j];
            }
        }
    }
    ZeroMemory(sBaseGameObjects, sizeof(sBaseGameObjects));

    // 删除所有移动结点
    POSITION pos = mMoveList.GetHeadPosition();
    while (pos != NULL) {
        delete mMoveList.GetNext(pos);
    }
    mMoveList.RemoveAll();
}

/**
 * 进入关卡。
 */
void Game::EnterLevel() {
    // 重绘游戏区域
    mDrawer->FillSolidRect(0, 0, Const::GAME_BOARD_WIDTH, Const::GAME_BOARD_HEIGHT, Const::GAME_BOARD_COLOR);

    // 删除所有物体
    this->Clear();

    // 获取关卡文件路径
    CString levelFilePath;
    levelFilePath.Format(Const::LEVEL_FILE_PATH, mLevel);

    // 读取关卡文件
    CFile levelFile;
    levelFile.Open(levelFilePath, CFile::modeRead, NULL);

    // 创建所有物体
    UINT read = 1;
    char buffer = 0;
    BaseGameObject::Type type;
    HDC dc = mDrawer->GetSafeHdc();
    mBoxCount = 0;
    for (int y = 0; y < Const::GAME_BOARD_GRID_COUNT_V; y++) {
        for (int x = 0; x < Const::GAME_BOARD_GRID_COUNT_H; x++) {
            read = levelFile.Read(&buffer, 1);
            if (buffer == '0') {
                continue;
            }
            if (buffer == '\r' || buffer == '\n') {
                x--;
                continue;
            }

            type = (BaseGameObject::Type)atoi(&buffer);

            // 创建箱子
            if (type == BaseGameObject::BOX) {
                sBaseGameObjects[y][x] = new Box(new BaseGameObject(dc, BaseGameObject::BACKGROUND, x, y), dc, type, x, y);
                mBoxCount++;
                continue;
            }

            // 创建小人
            if (type == BaseGameObject::MAN) {
                sBaseGameObjects[y][x] = mMan = new Man(new BaseGameObject(dc, BaseGameObject::BACKGROUND, x, y), dc, mGameBoard, type, x, y);
                continue;
            }

            // 创建背景、目的地、墙
            sBaseGameObjects[y][x] = new BaseGameObject(dc, type, x, y);
        }
    }

    // 关闭文件
    levelFile.Close();

    // 重置成员变量
    mMoveCount = 0;
    mStatus = PLAYING;

    // 刷新游戏区域
    mGameBoard->Invalidate(FALSE);

    // 发送通知
    mCallback->SendMessage(Const::UM_GOTO_LEVEL, mLevel);
}

/**
 * 游戏结束。
 * @param allOver   全部通关则为 TRUE，否则为 FALSE
 */
void Game::GameOver(BOOL allOver) {
    // 将状态设置为已结束
    mStatus = END;

    // 发送通知
    mCallback->SendMessage(Const::UM_GAME_OVER, allOver, 0);
}

/**
 * 获取当前关卡数。
 * @return 当前关卡数
 */
int Game::GetLevel() {
    return mLevel;
}

/**
 * 进入指定的关卡。
 * @param level 关卡数
 */
void Game::GotoLevel(int level) {
    if (level <= Const::LEVEL_COUNT) {
        mLevel = level;
        EnterLevel();
    }
}

/**
 * 进入下一关。
 */
void Game::GotoNextLevel() {
    if (mLevel < Const::LEVEL_COUNT) {
        mLevel++;
        EnterLevel();
    }
}

/**
 * 判断是否胜利。
 * @return 胜利则返回 TRUE，否则返回 FALSE
 */
BOOL Game::IsWon() {
    return mBoxCount == 0;
}

/**
 * 按键事件的响应方法。
 * @param key  按键
 * @return 事件在此被处理则返回 TRUE，否则返回 FALSE
 */
BOOL Game::OnKeyPressed(UINT key) {
    // 如果没在玩，则直接返回
    if (mStatus != PLAYING) {
        return FALSE;
    }

    // 如果按的不是方向键，则直接返回
    if (key < VK_LEFT || key > VK_DOWN) {
        return FALSE;
    }

    // 移动小人。如果不能移动，则直接返回
    BaseGameObject::MoveResult moveResult = BaseGameObject::CANNOT_MOVE;
    BaseGameObject* movedBox = mMan->Move(key, &moveResult);
    if (moveResult <= BaseGameObject::NO_MOVE) {
        return TRUE;
    }

    // 重绘游戏区域
    mGameBoard->Invalidate(FALSE);

    // 增加移动次数
    mMoveCount++;

    // 发送通知
    mCallback->SendMessage(Const::UM_MAN_MOVED, mMoveCount);

    // 判断是否胜利
    if (moveResult == BaseGameObject::ARRIVED_ON_DEST) {
        mBoxCount--;
        if (this->IsWon()) {
            BOOL allOver = mLevel == Const::LEVEL_COUNT;
            this->GameOver(allOver);
            if (!allOver) {
                this->GotoNextLevel();
            }
            return TRUE;
        }
    } else if (moveResult == BaseGameObject::LEFT_FROM_DEST) {
        mBoxCount++;
    }

    // 将本次移动加入列表
    mMoveList.AddTail(new MoveNode(movedBox, key));

    return TRUE;
}

/**
 * 重玩本关。
 */
void Game::Restart() {
    this->GotoLevel(mLevel);
}

/*
 * 回退，即撤消上一次操作。
 */
void Game::Undo() {
    if (mMoveCount == 0) {
        return;
    }

    // 计算方向（方向与最后一次移动的方向相反）
    UINT direction = 0;
    MoveNode* moveListNode = mMoveList.RemoveTail();
    switch (moveListNode->GetDirection()) {
        case VK_DOWN:
            direction = VK_UP;
            break;
        case VK_LEFT:
            direction = VK_RIGHT;
            break;
        case VK_RIGHT:
            direction = VK_LEFT;
            break;
        case VK_UP:
            direction = VK_DOWN;
            break;
    }

    // 移动小人
    BaseGameObject::MoveResult moveResult = BaseGameObject::MoveResult::CANNOT_MOVE;
    mMan->Move(direction, &moveResult);

    // 移动箱子
    BaseGameObject* box = moveListNode->GetBox();
    box->Move(direction, &moveResult);
    if (moveResult == BaseGameObject::MoveResult::LEFT_FROM_DEST) {
        mBoxCount++;
    } else if (moveResult == BaseGameObject::MoveResult::ARRIVED_ON_DEST) {
        mBoxCount--;
    }

    // 删除当前的移动结点
    delete moveListNode;

    // 发送通知
    mMoveCount--;
    mCallback->SendMessage(Const::UM_MAN_MOVED, mMoveCount);
}
